package org.geektimes.configuration.micro.config;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;
import org.geektimes.configuration.micro.config.converter.Converters;
import org.geektimes.configuration.micro.config.source.ConfigSources;

import java.util.*;
import java.util.stream.StreamSupport;

/**
 *
 */
public class DefaultConfig implements Config {

    private final ConfigSources configSources;
    private final Converters converters;

    private static Comparator<ConfigSource> configSourceComparator = new Comparator<ConfigSource>() {
        @Override
        public int compare(ConfigSource o1, ConfigSource o2) {
            return Integer.compare(o2.getOrdinal(),o1.getOrdinal());
        }
    };

    public DefaultConfig(ConfigSources configSources, Converters converters) {
        this.configSources = configSources;
        this.converters = converters;
    }

//    private void processConverter(ClassLoader classLoader) {
//        ServiceLoader<Converter> load = ServiceLoader.load(Converter.class, classLoader);
//        Iterator<Converter> iterator = load.iterator();
//        while (iterator.hasNext()) {
//            Converter next = iterator.next();
//            ParameterizedType type = (ParameterizedType) next.getClass().getGenericInterfaces()[0];
//            Type actualTypeArgument = type.getActualTypeArguments()[0];
//            converterMap.put(actualTypeArgument.getTypeName(), next);
//        }
//    }
//
//    private void processConfigSource(ClassLoader classLoader) {
//        ServiceLoader<ConfigSourceProvider> load = ServiceLoader.load(ConfigSourceProvider.class, classLoader);
//        Iterator<ConfigSourceProvider> iterator = load.iterator();
//        if (iterator.hasNext()){
//            ConfigSourceProvider next = iterator.next();
//            Iterable<ConfigSource> iterable = next.getConfigSources(classLoader);
//            iterable.forEach(configSources::add);
//        }
//        configSources.sort(configSourceComparator);
//    }

    @Override
    public <T> T getValue(String propertyName, Class<T> propertyType) {
        String propertyValue = this.getPropertyValue(propertyName);
        Converter<T> converter = this.doGetConverter(propertyType);
        // 转换成目标类型
        return converter.convert(propertyValue);
    }

    protected  <T> Converter<T> doGetConverter(Class<T> propertyType) {
        List<Converter> converterList = this.converters.getConverters(propertyType);
        return converterList.get(0);
    }

    protected String getPropertyValue(String propertyName) {
        String propertyValue = null;
        for (ConfigSource configSource : configSources) {
            propertyValue = configSource.getValue(propertyName);
            if (propertyValue != null) {
                break;
            }
        }
        return propertyValue;
    }

    @Override
    public ConfigValue getConfigValue(String propertyName) {
        return null;
    }

    @Override
    public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
        T value = this.getValue(propertyName, propertyType);
        return Optional.ofNullable(value);
    }

    @Override
    public Iterable<String> getPropertyNames() {
        return StreamSupport.stream(configSources.spliterator(), false)
                .map(ConfigSource::getPropertyNames)
                .collect(LinkedHashSet::new, Set::addAll, Set::addAll);
    }

    @Override
    public Iterable<ConfigSource> getConfigSources() {
        return configSources;
    }

    @Override
    public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
        Converter<T> converter = this.doGetConverter(forType);
        return converter == null ? Optional.empty() : Optional.of(converter);
    }

    @Override
    public <T> T unwrap(Class<T> type) {
        return null;
    }
}
